class_name DataMap

## 承载地形、传送点、玩家、怪物、npc、掉落物等对象的容器

# 基本信息
var id: String
var name: String
# 地区id（同时也是地区名称）
var region_id: String
# 是否为主城
var main_town: bool = false
# 地图位置，归一化坐标（0-1）
var position: Vector2

#region 怪物刷新
# 当前地图怪物刷新点
var map_monster_refresh_pos: Array[MonsterRefreshPos] = []
# 怪物刷新信息 key:monster_id value:config
var monster_config_dic = {}
# 怪物刷新时间信息 key:monster_id value:刷新间隔开始时间
var monster_refresh_time_dic = {}
# 怪物技能信息 key:monster_skill_id value:技能信息
var monster_skill_dic = {}
# 怪物特殊效果配置信息 key:effect_id value:配置信息
var effect_config_dic = {}
#endregion

# 数据
var data_player: DataPlayer
var data_npcs = {}
var data_monsters = {}
var data_drop_things = {}
var data_portals = {}
var data_floors = {}
# 分身数据 key:player_id value:DataPlayer
var data_player_clone: Dictionary = {}

var monster_manager = MonsterManager.new()
var drop_thing_manager: DropThingManager

## 已经击杀当前地图的怪物个数,key:怪物id value:击杀数量，用于解锁某些传送点限制
var kill_monster_count_dic = {}

# 是否进入过地图进行过探索
var is_explored: bool = false

#region 无尽之塔相关属性
var is_endless: bool = false
# 层数
var endless_layer: int = 0
# 层数max
const ENDLESS_LAYER_MAX: int = 9
# 怪物强化倍数
var monster_upgrade: float = 0
# 怪物数量
var monster_count: int = 0
# 怪物id列表
var monster_id_list: Array[String] = []
# 奖励列表
var reward_list: Array[Dictionary] = []
#endregion


# 进入boss战斗标记
var is_boss_combat_start = false

## 首个玩家进入
signal first_player_entered

signal monster_added(data_monster: DataMonster,position: Vector2)

signal drop_thing_dropped(drop_thing: DataBagItem, target: DataRole, offset_index: int)

signal drop_thing_picked(data_map: DataMap, drop_thing: DataBagItem)

signal npc_added(data_npc: DataNPC)

signal player_killed_monster(data_player: DataPlayer, target: DataMonster)

signal player_added(data_map: DataMap, data_player: DataPlayer)

signal player_removed(data_map: DataMap, data_player: DataPlayer)

signal map_explored(data_map: DataMap)

signal endless_ended(data_map: DataMap)

signal boss_combat_started(data_map: DataMap, data_monster: DataMonster)

signal boss_killed(data_map: DataMap, data_monster: DataMonster)

signal monster_ready_blacked(data_map: DataMap, data_monster: DataMonster)

signal player_clone_added(data_map: DataMap, data_player: DataPlayer)

signal player_clone_removed(data_map: DataMap, data_player: DataPlayer)

## 对外接口：玩家加入地图
func add_player(_data_player: DataPlayer):
	data_player = _data_player

	first_player_entered.emit()
	data_player.map_id = id
	# 设置地图探索状态
	if not is_explored:
		is_explored = true
		map_explored.emit(self)
	# 玩家停止休息
	data_player.stop_rest()
	# 监听玩家拾取
	data_player.pick_up_executed.connect(_on_player_pick_execute)
	# 监听玩家受伤
	data_player.role_hurted.connect(_on_player_hurted)
	# 发出玩家加入地图信号
	player_added.emit(self, data_player)


## 添加分身
func add_player_clone(_player_clone: DataPlayer):
	data_player_clone[_player_clone.player_id] = _player_clone
	_player_clone.map_id = id

	# 发出玩家加入地图信号
	player_clone_added.emit(self, _player_clone)


func remove_player(_player_id: String):
	# 取消玩家监听
	data_player.pick_up_executed.disconnect(_on_player_pick_execute)
	# 取消玩家受伤监听
	data_player.role_hurted.disconnect(_on_player_hurted)
	# 发出玩家离开地图信号
	player_removed.emit(self, data_player)
	# 玩家状态重置
	data_player.reset()
	data_player = null

	# 清除分身
	for player_clone_id in data_player_clone.keys():
		if not data_player_clone.has(player_clone_id):
			continue
		var player_clone = data_player_clone[player_clone_id]
		player_clone.pick_up_executed.disconnect(_on_player_pick_execute)
		# 取消玩家受伤监听
		player_clone.role_hurted.disconnect(_on_player_hurted)
		# 发出玩家离开地图信号
		player_clone_removed.emit(self, player_clone)
		# 玩家状态重置
		player_clone.reset()
		data_player_clone.erase(player_clone_id)

	# 无尽之塔退出，则清空怪物
	if is_endless:
		_reset()
	
	# 重置怪物状态（）
	for monster in data_monsters.values():
		# 删除玩家的黑化召唤物
		if monster.is_black_monster:
			remove_monster(monster)
			continue
		# 如果怪物是boss召唤物，则清除
		if monster.is_spawn_monster:
			remove_monster(monster)
			monster.kill_role()
			continue
		if monster.reset_status:
			monster.reset()
			# boss状态重置时，重置战斗开始状态
			is_boss_combat_start = false


## 移除分身
func remove_player_clone(_data_player_clone: DataPlayer):
	data_player_clone.erase(_data_player_clone.player_id)
	# 玩家状态重置
	_data_player_clone.reset()
	# 发出分身离开地图信号
	player_clone_removed.emit(self, _data_player_clone)


func _reset():
	# 清空怪物
	for data_monster in data_monsters.values():
		remove_monster(data_monster)
		data_monster.kill_role()
	
	endless_layer = 0


func remove_monster(data_monster: DataMonster):
	# 解除怪物死亡监听
	data_monster.role_hurted.disconnect(_on_monster_hurted)
	data_monster.role_dead.disconnect(_on_monster_dead)
	# 删除数据
	data_monsters.erase(data_monster.monster_unique_id)


## 对外接口：添加传送点
func add_portal(data_portal: DataPortal):
	data_portals[data_portal.portal_id] = data_portal


## 获取所有传送点
func get_portals():
	return data_portals


func add_npc(data_npc: DataNPC):
	data_npcs[data_npc.id] = data_npc
	npc_added.emit(data_npc)


## 对外接口：刷新怪物
func refresh_monsters():
	for refresh_pos in map_monster_refresh_pos:
		# 如果怪物已经死亡，则清空刷新点的怪物
		if refresh_pos.data_monster and refresh_pos.data_monster.is_dead:
			# 记录怪物刷新间隔开始时间
			monster_refresh_time_dic[refresh_pos.data_monster.monster_id] = Time.get_unix_time_from_system()
			# 清空刷新点怪物
			refresh_pos.data_monster = null

		if not refresh_pos.data_monster:
			var monster_id = refresh_pos.random_monster_id(monster_refresh_time_dic)
			if monster_id:
				var monster_config = monster_config_dic[monster_id]
				# 精英怪的概率
				var is_elite = randf() < 0.01
				# 创建怪物对象
				var monster = monster_manager.create_monster(
					monster_id, is_elite, monster_config,monster_skill_dic,effect_config_dic
				)
				refresh_pos.data_monster = monster
				# 添加怪物
				add_monster(monster,refresh_pos.pos)


func _on_monster_hurted(data_role:DataRole,data_damage:DataDamage):
	if data_role is DataMonster:
		if data_player:
			data_player.cause_damage(data_damage)
		# 如果boss首次受伤，则开始计算击杀时间
		_check_boss_combat_start(data_role)	


func _on_monster_dead(data_role:DataRole):
	# 增加击杀怪物数量
	if data_role is DataMonster:
		kill_monster_count_dic[data_role.monster_id] = kill_monster_count_dic.get(data_role.monster_id, 0) + 1
	# 发出玩家击杀怪物信号
	player_killed_monster.emit(data_player, data_role)
	# 给玩家增加经验
	if not is_endless and not data_role.is_black_monster:
		if data_player:
			data_player.add_exp(data_role.exp)
	# 生成掉落物
	if not data_role.is_black_monster:
		_create_drop_thing(data_role)
	
	# 删除数据
	remove_monster(data_role)
	# 无尽之塔判断怪物是否全部死亡
	if is_endless:
		# 判断是否还有怪物
		if data_monsters.is_empty():
			# 所有怪物死亡，结束无尽之塔
			end_endless()
	# 如果怪物是boss，则发送boss死亡信号
	if data_role.is_boss():
		boss_killed.emit(self, data_role)


func add_monster(_monster: DataMonster,_position: Vector2):
	data_monsters[_monster.monster_unique_id] = _monster
	_monster.current_map_id = id
	# 发出怪物添加信号
	monster_added.emit(_monster,_position)
	# 监听怪物受伤
	_monster.role_hurted.connect(_on_monster_hurted)
	# 监听怪物死亡
	_monster.role_dead.connect(_on_monster_dead)

	# 如果添加了boss，需要重置boss的战斗状态
	if _monster.is_boss():
		is_boss_combat_start = false


func has_monster_refresh_pos():
	return not map_monster_refresh_pos.is_empty()

	
## 攻击技能作用到目标
func player_attack_skill_effect(
	_data_player: DataPlayer,
	target: DataMonster,
	skill: DataBaseSkill,
	direction: Vector2 = Vector2.RIGHT,
	effect_position: Vector2 = Vector2.ZERO,
	target_monster_count: int = 1
):
	if target.is_dead:
		return
	# 查看技能类型
	if skill is DataElementAttackSkill:
		pass
	elif skill is DataEffectAttackSkill:
		pass
	elif skill is DataAttackSkill:
		# 计算伤害
		var damage_value = skill.get_damage_value()
		if _is_attack_hit(_data_player, target):
			var damage : DataDamage
			if skill.damage_value_type == Constants.VALUE_TYPE_PERCENT:
				damage = _create_percent_damage(skill, damage_value, _data_player, target, target_monster_count)
			else:
				damage = _create_number_damage(skill, damage_value, _data_player, target)
			damage.direction = direction

			# 造成伤害
			target.get_hurt(damage)
			# 标记攻击来源
			target.add_attack_source(_data_player.player_id)

			# 造成伤害后的特效处理
			_after_damage_effect(_data_player,target, skill, damage,direction,effect_position)
		else:
			var damage = DataDamage.new(skill.damage_type, DataDamage.SOURCE_TYPE.PLAYER, 0)
			damage.direction = direction
			target.get_hurt(damage)


		# 判断目标是否死亡，给玩家增加经验
		if target.is_dead:
			# 判断击杀类的特殊效果
			_check_kill_monster_effect(_data_player, target)
			# 判断“暗黑魔法”效果
			_check_black_monster_effect(_data_player, target)


func _check_black_monster_effect(
	_data_player: DataPlayer, 
	_target: DataMonster
):
	if _data_player and _data_player.has_effect("effect_000045"):
		# 发出黑化怪物准备黑化的信号（是否能黑化由黑化怪物管理器决定）
		monster_ready_blacked.emit(self, _target)


func _after_damage_effect(
	_data_player: DataPlayer, 
	_target: DataMonster,
	_skill: DataBaseSkill, 
	damage: DataDamage,
	direction: Vector2,
	effect_position: Vector2 = Vector2.ZERO
):
	if _data_player.has_effect("effect_000018"):
		var effect = _data_player.get_effect("effect_000018")
		if effect.value_type == Constants.VALUE_TYPE_PERCENT:
			var recover_mp = damage.value * effect.value
			_data_player.recover_mp(recover_mp)
	if _data_player.has_effect("effect_000019"):
		var attack_attach_poison_effect = _data_player.get_effect("effect_000019")
		if attack_attach_poison_effect.value_type == Constants.VALUE_TYPE_PERCENT:
			# 百分之20的概率中毒
			var hit_rate = 0.2
			# 概率加成
			hit_rate += luck_rate_add()
			# 概率判断
			if randf() < hit_rate:
				var damage_value = _data_player.get_final_attack(-1) * attack_attach_poison_effect.value
				damage_value = max(damage_value, 1)
				var buff = _create_poison_buff(damage_value)
				_target.add_buff(buff)
	if _data_player.has_effect("effect_000022"):
		var effect = _data_player.get_effect("effect_000022")
		var hit_rate = effect.value
		# 概率加成
		hit_rate += luck_rate_add()
		if randf() < hit_rate:
			# 伤口撕裂
			var deep_damage_value = max(1,_data_player.get_final_details().attack * 0.01)
			var deep_damage_effect = _create_deep_damage_effect(deep_damage_value)
			_target.add_effect(deep_damage_effect)
	if _data_player.has_effect("effect_000025"):
		var effect = _data_player.get_effect("effect_000025")
		var hit_rate = effect.value
		# 概率加成
		hit_rate += luck_rate_add()
		if randf() < hit_rate:
			# 护甲撕裂
			_target.add_armor_break_value(5)
	if _data_player.has_effect("effect_000005"):
		# 只有常规攻击技能会触发该效果
		if _skill.id.begins_with("skill_"):	
			var effect = _data_player.get_effect("effect_000005")
			# 10%的概率命中
			var hit_rate = 0.1
			# 概率加成
			hit_rate += luck_rate_add()
			# 概率判断
			if randf() < hit_rate:
				print("赤焰爆炸：",effect.value)
				var skill = effect.get_special_skill()
				skill.effect_position = effect_position
				if skill != null:
					_data_player.execute_skill_no_cd(skill)
	if _data_player.has_effect("effect_000030"):
		# 只有常规攻击技能会触发该效果
		if _skill.id.begins_with("skill_"):	
			var effect = _data_player.get_effect("effect_000030")
			# 消耗MP
			if _data_player.mp >= effect.mp_cost:
				_data_player.recover_mp(-effect.mp_cost)
				print("破空剑气：",effect.value)
				var skill = effect.get_special_skill()
				skill.direction = direction
				skill.damage_value_type = Constants.VALUE_TYPE_NUMBER
				skill.damage_level = [[damage.value * effect.value]]
				_data_player.execute_skill_no_cd(skill)
			
	# 自损八千
	if _data_player.has_effect("effect_000004"):
		if _skill.id.begins_with("skill_"):	
			# 承受自损八千伤害的10%
			for damage_detail in damage.damage_details:
				if damage_detail.name == "自损八千":
					var damage_value = damage_detail.value * 0.1
					var self_damage = DataDamage.new(
						DataDamage.TYPE.MAGICAL, 
						DataDamage.SOURCE_TYPE.MONSTER, 
						damage_value
					)
					_data_player.get_hurt(self_damage)
					print("自损八千受伤:",self_damage.value)
	# 致命弱点
	if _data_player.has_effect("effect_000038"):
		if _skill.id.begins_with("skill_"):	
			var effect = _data_player.get_effect("effect_000038")
			var hit_rate = effect.value
			# 概率加成
			hit_rate += luck_rate_add()
			if randf() < hit_rate:
				# 直接消减目标当前生命值
				var damage_value = _target.hp * 0.01
				_target.get_hurt(DataDamage.new(DataDamage.TYPE.REAL, DataDamage.SOURCE_TYPE.PLAYER, damage_value))
	# 流星陨落
	if _data_player.has_effect("effect_000040"):
		# 只有常规攻击技能会触发该效果
		if _skill.id.begins_with("skill_"):	
			if Time.get_ticks_msec() - _data_player.meteor_fall_trigger_time >= _data_player.meteor_fall_trigger_interval * 1000:
				var effect = _data_player.get_effect("effect_000040")
				# 3%的概率命中
				var hit_rate = 0.03
				# 概率加成
				hit_rate += luck_rate_add()
				var rate = randf()
				print("流星陨落概率判定：",rate)
				# 概率判断
				if rate < hit_rate:
					# 只有命中时才会计算CD
					_data_player.meteor_fall_trigger_time = Time.get_ticks_msec()

					print("流星陨落：",effect.value)
					var skill = effect.get_special_skill()
					# 随机偏移位置
					var offset_x = randf_range(-50, 50)
					var offset_y = randf_range(-50, 50)
					skill.effect_position = Vector2(275 + offset_x, 275 + offset_y)
					if skill != null:
						_data_player.execute_skill_no_cd(skill)

	# 汲血之力
	if damage.value > 0 and _data_player.has_effect("effect_000043"):
		var effect = _data_player.get_effect("effect_000043")
		var recover_hp = max(1,damage.value * effect.value)
		_data_player.recover_hp_with_signal(recover_hp)

	# 爆炎射线
	if _data_player.has_effect("effect_000049"):
		# 只有常规攻击技能会触发该效果
		if _skill.id.begins_with("skill_"):	
			# 20%的击中概率
			var hit_rate = 0.2
			hit_rate += luck_rate_add()
			var rate = randf()
			print("爆炎射线概率判定:",rate," ",rate <= hit_rate)
			if rate <= hit_rate:
				var effect = _data_player.get_effect("effect_000049")
				# 消耗MP
				if _data_player.mp < effect.mp_cost:
					return
				_data_player.recover_mp(-effect.mp_cost)
		
				var skill = effect.get_special_skill()
				skill.direction = direction

				_data_player.execute_skill_no_cd(skill)


## 获取概率加成
func luck_rate_add()-> float:
	var rate_add = 0.0
	if data_player:
		# 运气加持
		if data_player.has_effect("effect_000035"):
			var effect_luck = data_player.get_effect("effect_000035")
			rate_add += data_player.get_final_ability().luck * effect_luck.value
		# 福星高照 运气加持翻倍
		if data_player.has_effect("effect_000062"):
			var effect_luck = data_player.get_effect("effect_000062")
			if effect_luck.is_suit_invoked():
				rate_add += rate_add * effect_luck.get_suit_value()
	return rate_add


func _create_deep_damage_effect(value: int) -> DataEffect:
	# “撕裂”效果
	var deep_damage_effect = DataEffect.new("effect_000023", "deep_damage")
	deep_damage_effect.value = value
	deep_damage_effect.value_type = Constants.VALUE_TYPE_NUMBER

	return deep_damage_effect


func _create_poison_buff(value: int) -> DataBuff:
	# “中毒”效果，用时间戳作为id，方便叠加
	var buff_id = str(Time.get_ticks_usec())
	var poison_effect = DataEffect.new(buff_id, "poison")
	poison_effect.value = value
	# “中毒”buff持续5秒
	var buff = DataBuff.new()
	buff.id = buff_id
	buff.name = "中毒"
	buff.duration = 5 / SingletonGame.speed
	buff.remaining_time = buff.duration
	buff.add_effect(poison_effect)
	return buff


func _create_drop_thing(target: DataMonster):
	# 如果怪物不掉落物品，则不掉落
	if not target.is_drop_item:
		return

	# 无尽之塔不会掉落
	if is_endless:
		return

	# 掉落物的偏移值，方便展示
	var offset_index = 0
	var drop_things = drop_thing_manager.get_drop_things(target.monster_id,target.is_elite,data_player)
	for drop_thing in drop_things:
		# 放入地图数据中
		data_drop_things[drop_thing.uuid] = drop_thing

		drop_thing_dropped.emit(drop_thing, target, offset_index)
		offset_index += 1


## 判断击杀类的特殊效果
func _check_kill_monster_effect(_data_player: DataPlayer, _target: DataMonster):
	if _data_player.has_effect("effect_000003"):
		var data_effect = _data_player.get_effect("effect_000003")
		_data_player.recover_hp_rate_with_signal(data_effect.value)


func _create_percent_damage(
	skill: DataBaseSkill,
	_damage_rate: float,
	_data_player: DataPlayer,
	target: DataMonster,
	target_monster_count: int = 1
) -> DataDamage:
	# 计算伤害值
	var damage_value = 0
	# 伤害详情统计
	var damage_details: Array[DataDamage.DamageDetail] = []
	
	#region 攻击力
	# 攻击力波动的比率 0-1之间
	var attack_value_rate = randf()
	# “毫无章法”对攻击力波动随机值影响
	if (skill.id == "skill_000000" or skill.id == "skill_000201" ) \
		and _data_player.has_effect("effect_000037"):
		# 攻击目标1-10，目标越少，攻击力波动值越大
		attack_value_rate *=  max(0,((10 - target_monster_count) / 10.0))
	# “流星陨落”伤害为固定比例不波动
	if skill.id == "meteor_fall":
		attack_value_rate = -1

	var attack_value = 0
	# 根据不同加成类型计算最终技能加成的伤害值
	if skill.damage_source_type == 0:
		# 攻击力加成
		# 从玩家的最小攻击力到最大攻击力之间随机一个值
		attack_value = _data_player.get_final_attack(attack_value_rate)
	elif skill.damage_source_type == 1:
		# 魔法力加成
		# 从玩家的最小魔法攻击力到最大魔法攻击力之间随机一个值
		attack_value = _data_player.get_final_magic_attack(attack_value_rate)
	elif skill.damage_source_type == 2:
		# 攻击力+魔法力加成
		# 从玩家的最小攻击力到最大攻击力之间随机一个值
		var phy_attack_value = _data_player.get_final_attack(attack_value_rate)
		# 从玩家的最小魔法攻击力到最大魔法攻击力之间随机一个值
		var magic_attack_value = _data_player.get_final_magic_attack(attack_value_rate)
		attack_value = phy_attack_value + magic_attack_value
	elif skill.damage_source_type == 3:
		# 血量百分比加成
		attack_value = _data_player.get_final_details().max_hp
	#endregion

	damage_details.append(DataDamage.DamageDetail.new(skill.name, attack_value * _damage_rate, _damage_rate))
	#region 技能百分比伤害加成，各种加成+算，这些加成只吃基础的攻击力面板
	var skill_damage_rate = 0
	# 技能增幅加成
	if _data_player.has_skill_enhance(skill.id):
		var skill_enhance = _data_player.get_skill_enhance(skill.id)
		skill_damage_rate += skill_enhance.damage
		# 评估该效果最终的伤害值
		var record_damage_value = attack_value * _damage_rate * skill_enhance.damage
		damage_details.append(DataDamage.DamageDetail.new("技能增幅", record_damage_value, skill_enhance.damage))
	# 技能伤害加成
	if _data_player.is_match_mp_cost_enhance():
		var data_effect = _data_player.get_mp_cost_enhance()
		skill_damage_rate += data_effect.value
		# 评估该效果最终的伤害值
		var record_damage_value = attack_value * _damage_rate * data_effect.value
		damage_details.append(DataDamage.DamageDetail.new("法力涌动", record_damage_value, data_effect.value))
		# 重置“法力涌动”累计值
		_data_player.reset_mp_cost_enhance()
	# “血气爆发”特殊效果
	if _data_player.is_match_hp_cost_enhance():
		var data_effect = _data_player.get_hp_cost_enhance()
		skill_damage_rate += data_effect.value
		# 评估该效果最终的伤害值
		var record_damage_value = attack_value * _damage_rate * data_effect.value
		damage_details.append(DataDamage.DamageDetail.new("血气爆发", record_damage_value, data_effect.value))
		# 重置“血气爆发”累计值
		_data_player.reset_hp_cost_enhance()
	# “奋力一击”蓄力效果
	if skill.id == "skill_000001" and _data_player.has_effect("effect_000026"):
		var effect = _data_player.get_effect("effect_000026")
		# 根据蓄力段数结算伤害
		var charge_damage_rate = effect.value * skill.charge_num
		skill_damage_rate += charge_damage_rate
		# 评估该效果最终的伤害值
		var record_damage_value = attack_value * _damage_rate * charge_damage_rate
		damage_details.append(DataDamage.DamageDetail.new("拔刀蓄力", record_damage_value, charge_damage_rate))
	

	_damage_rate *= 1 + skill_damage_rate
	print("技能伤害加成：",skill_damage_rate)
	#endregion

	# 计算伤害值
	damage_value = attack_value * _damage_rate

	#region 和技能系数无关的伤害加成
	var effect_damage_value = _effect_damage_value(_data_player,target,damage_details,skill,attack_value)
	damage_value += effect_damage_value
	#endregion

	#region 最终伤害结算加成
	var final_damage_value = _final_damage_value(_data_player,target,damage_value,damage_details)
	damage_value += final_damage_value
	#endregion

	# 判断分身的伤害比例
	if _data_player.is_copy:
		var copy_reduce_damage = damage_value * _player_copy_damage_rate(_data_player)
		damage_value -= copy_reduce_damage
	
	# 根据伤害类型计算防御减伤
	if skill.damage_type == 0:
		# 计算目标防御减伤
		var defense_reduction_value = target.attribute.defense_reduction_value()
		damage_value -= defense_reduction_value
		damage_details.append(DataDamage.DamageDetail.new("防御减伤", -defense_reduction_value,0))
	elif skill.damage_type == 1:
		# 计算目标魔法防御减伤
		var defense_reduction_value = target.attribute.magic_defense_reduction_value()
		damage_value -= defense_reduction_value
		damage_details.append(DataDamage.DamageDetail.new("魔法防御减伤", -defense_reduction_value,0))
	
	# 伤害值最小为1
	damage_value = max(1, damage_value)

	var damage = DataDamage.new(skill.damage_type, DataDamage.SOURCE_TYPE.PLAYER, damage_value)
	damage.value_show_rate = _data_player.get_show_rate(attack_value_rate)
	damage.attack_value = attack_value
	damage.damage_details = damage_details
	# 战争践踏的统计信息需要特殊处理
	if skill.id == "war_stomp":
		damage.damage_info_name = "战争践踏"

	#print("伤害详情：",damage_details)
	return damage


func _final_damage_value(
	_data_player: DataPlayer,
	_data_monster: DataMonster,
	_damage_value: int,
	_damage_details: Array[DataDamage.DamageDetail]
) -> int:
	var final_add_value = 0
	# 血脉压制 BOSS
	if _data_monster.is_boss() and _data_player.has_effect("effect_000056"):
		var effect = _data_player.get_effect("effect_000056")
		# 每100点HP为计量单位
		var rate = _data_player.get_final_details().max_hp / 100.0
		var add_value = effect.value * _damage_value * rate
		final_add_value += add_value
		_damage_details.append(DataDamage.DamageDetail.new("血脉压制", add_value, effect.value))
	# 暴力美学
	if _data_player.has_effect("effect_000033"):
		var effect = _data_player.get_effect("effect_000033")
		if effect.value_type == Constants.VALUE_TYPE_PERCENT:
			var power_add_damage = _data_player.get_final_ability().power * effect.value

			var add_value = _damage_value * power_add_damage
			final_add_value += add_value
			_damage_details.append(DataDamage.DamageDetail.new("暴力美学", add_value,power_add_damage))

	return final_add_value


## 分身的伤害系数
func _player_copy_damage_rate(_data_player: DataPlayer)-> float:
	if not _data_player.has_effect("effect_000046"):
		return 1.0
	var effect = _data_player.get_effect("effect_000046")
	return 1 - effect.value


func _create_number_damage(
	skill: DataBaseSkill,
	_damage_value: int,
	_data_player: DataPlayer,
	target: DataMonster
) -> DataDamage:
	# 计算伤害值
	var damage_value = _damage_value
	# 伤害详情统计
	var damage_details: Array[DataDamage.DamageDetail] = []
	damage_details.append(DataDamage.DamageDetail.new(skill.name, _damage_value, 0))
	# 技能增幅加成
	if _data_player.has_skill_enhance(skill.id):
		var skill_enhance = _data_player.get_skill_enhance(skill.id)
		var enhance_damage_value = _damage_value * skill_enhance.damage
		_damage_value += enhance_damage_value
		damage_details.append(DataDamage.DamageDetail.new("技能增幅", enhance_damage_value, skill_enhance.damage))

	# 计算最终伤害值（存在各种伤害加成的计算）
	var effect_damage_value = _effect_damage_value(_data_player, target, damage_details, skill,0)
	damage_value += effect_damage_value

	var defense_reduction_value = 0
	if skill.damage_type == 0:
		# 物理伤害
		# 计算目标防御减伤
		defense_reduction_value = target.attribute.defense_reduction_value()
	elif skill.damage_type == 1:
		# 魔法伤害
		# 计算目标魔法防御减伤
		defense_reduction_value = target.attribute.magic_defense_reduction_value()
	damage_value -= defense_reduction_value
	damage_details.append(DataDamage.DamageDetail.new("防御减伤", -defense_reduction_value,0))
	
	damage_value = max(1, damage_value)
	var damage = DataDamage.new(skill.damage_type, DataDamage.SOURCE_TYPE.PLAYER, damage_value)
	damage.damage_details = damage_details
	
	return damage


func _effect_damage_rate(
	_data_player: DataPlayer, 
	_target: DataMonster
) -> float:
	var effect_damage_rate = 0
	return effect_damage_rate


func _effect_damage_value(
	_data_player: DataPlayer, 
	_target: DataMonster, 
	_damage_details: Array[DataDamage.DamageDetail],
	_skill: DataBaseSkill,
	_attack_value: int
) -> float:
	var effect_damage_value = 0
	# 巨人之力
	if _data_player.has_effect("effect_000011"):
		# 只有常规攻击技能会触发该效果
		if _skill.id.begins_with("skill_"):
			# 最大生命值相关伤害
			var effect = _data_player.get_effect("effect_000011")
			if effect.value_type == Constants.VALUE_TYPE_PERCENT:
				var max_hp_damage = _data_player.get_final_details().max_hp * effect.value
				print("最大生命值附加伤害：",max_hp_damage)
				effect_damage_value += max_hp_damage
				_damage_details.append(DataDamage.DamageDetail.new("巨人之力", max_hp_damage,0))
			else:
				# 未实现
				pass
	# 背水一战
	if _data_player.has_effect("effect_000012"):
		# 只有常规攻击技能会触发该效果
		if _skill.id.begins_with("skill_"):
			var effect = _data_player.get_effect("effect_000012")
			if effect.value_type == Constants.VALUE_TYPE_PERCENT:
				# 损失生命值相关伤害
				var lost_hp = _data_player.get_final_details().max_hp - _data_player.hp
				# 每损失100点生命值，增加value点伤害
				var lost_hp_damage = lost_hp * effect.value
				print("损失生命值附加伤害：",lost_hp_damage)
				effect_damage_value += lost_hp_damage
				_damage_details.append(DataDamage.DamageDetail.new("背水一战", lost_hp_damage,0))
			else:
				# 未实现
				pass
	# 撕裂
	if _target.has_effect("effect_000023"):
		var effect = _target.get_effect("effect_000023")
		if effect.value_type == Constants.VALUE_TYPE_NUMBER:
			#print("撕裂特效附加伤害：",effect.value)
			effect_damage_value += effect.value
			_damage_details.append(DataDamage.DamageDetail.new("撕裂", effect.value,0))
		else:
			# 未实现
			pass
	# 自损八千
	if _data_player.has_effect("effect_000004"):
		# 只有常规攻击技能会触发该效果
		if _skill.id.begins_with("skill_"):
			var effect = _data_player.get_effect("effect_000004")
			if effect.value_type == Constants.VALUE_TYPE_PERCENT:
				var record_damage_value = _attack_value * effect.value
				effect_damage_value +=  record_damage_value
				_damage_details.append(DataDamage.DamageDetail.new("自损八千", record_damage_value,effect.value))
			else:
				# 未实现
				pass
	return effect_damage_value


func _is_attack_hit(attack: DataRole, target: DataRole):
	# 根据玩家命中率和目标闪避率，计算是否命中，命中率计算公式：命中率 = 1 + （玩家命中值 - 目标闪避值）/ 100.0
	var accuracy_rate = 1 + (attack.attribute.final_details.accuracy - target.attribute.final_details.evasion) / 100.0
	# 如果命中率小于0，则不命中
	if accuracy_rate < 0:
		accuracy_rate = 0
	# 如果命中率大于100，则命中率为100
	if accuracy_rate > 1:
		accuracy_rate = 1
	# 玩家的“运气加持”效果对命中率的加持
	if attack is DataPlayer:
		accuracy_rate += luck_rate_add()
	# 玩家的“运气加持”效果对回避率的加持
	if target is DataPlayer:
		accuracy_rate -= luck_rate_add()
	# 随机0-1之间的一个数，如果小于命中率，则命中，否则不命中
	var accuracy_hit = randf()
	if accuracy_hit < accuracy_rate:
		return true
	return


## Buff技能作用到目标
func _player_buff_skill_effect(data_player: DataPlayer, target: DataPlayer, skill: DataBaseSkill):
	if skill is DataAttriBuffSkill:
		pass
	elif skill is DataEffectBuffSkill:
		pass
	elif skill is DataBuffSkill:
		pass


## 处理掉落物的拾取逻辑
func _process_drop_thing_pick(_data_player: DataPlayer):
	# 遍历地图数据中的掉落物
	for drop_thing in data_drop_things.values():
		# 设置归属者为data_player
		if drop_thing.owner == null:
			drop_thing_picked.emit(self, drop_thing)
			# 一次只能拾取一个物品
			#break


func remove_drop_thing(drop_thing: DataBagItem):
	drop_thing.owner = data_player
	# 从数据中删除
	data_drop_things.erase(drop_thing.uuid)


func _on_player_pick_execute(data_player: DataPlayer):
	# print("_on_player_pick_execute:",data_player.player_name)
	_process_drop_thing_pick(data_player)


func _on_player_hurted(data_player: DataPlayer, data_damage: DataDamage):
	print("_on_player_hurted:",data_damage.value)
	# 检查当前buff
	for buff in data_player.buff_dic.values():
		for effect in buff.get_all_effects():
			if effect.type == DataEffect.DefenseCounterDamage and effect.is_active:
				if Time.get_ticks_msec() - effect.last_invoke_time > effect.invoke_interval * 1000:
					effect.last_invoke_time = Time.get_ticks_msec()

					# 防御反伤，每10点防御力，造成value伤害
					var defense_damage = data_player.get_final_details().defense * effect.value / 10.0
					print("触发防御反伤：",defense_damage)
					# 给半径范围内的怪物造成伤害(等同于执行一个AOE技能)
					var skill = _on_create_defense_counter_damage_skill(defense_damage,effect.radius)
					data_player.execute_skill(skill)


func _on_create_defense_counter_damage_skill(defense_damage: int, radius: int):
	var skill = DataAttackSkill.new(DataEffect.DefenseCounterDamage,"attack")
	skill.name = "防御反伤"
	skill.damage_type = DataDamage.TYPE.PHYSICAL
	skill.target_type = 1
	skill.radius = radius
	skill.count = Constants.INF
	skill.level = 1
	skill.damage_level = [[defense_damage]]
	skill.mp_cost_level = [0]
	skill.damage_value_type = Constants.VALUE_TYPE_NUMBER
	return skill


func on_monster_skill_executed(
	data_monster: DataMonster,
	target: DataRole,
	skill: DataBaseSkill,
	direction: Vector2 = Vector2.RIGHT
):
	if skill is DataAttackSkill:
		if _is_attack_hit(data_monster, target):
			# 怪物攻击力
			var attack_value = data_monster.attribute.final_details.attack
			# 怪物技能伤害系数
			var skill_damage_rate = skill.get_damage_value()

			# 最终伤害值
			var damage_value = attack_value * skill_damage_rate
			# 防御减伤
			var defense_reduction_value = target.attribute.defense_reduction_value()
			damage_value -= defense_reduction_value
			# 伤害值最小为1
			damage_value = max(1, damage_value)
			var damage = DataDamage.new(DataDamage.TYPE.PHYSICAL,DataDamage.SOURCE_TYPE.MONSTER, damage_value)
			damage.direction = direction
			# 判断目标是否为黑化怪物，如果是，则记录黑化怪物的伤害信息
			if data_monster.is_black_monster:
				var damage_details: Array[DataDamage.DamageDetail] = []
				damage_details.append(DataDamage.DamageDetail.new("暗黑魔法", damage.value,skill_damage_rate))
				damage.damage_details = damage_details
			target.get_hurt(damage)

			# 如果目标是普通怪物且死亡，则判断“暗黑魔法”
			if target is DataMonster and not target.is_black_monster and target.is_dead:
				_check_black_monster_effect(data_player,target)
		else:
			var damage = DataDamage.new(DataDamage.TYPE.PHYSICAL,DataDamage.SOURCE_TYPE.MONSTER, 0)
			damage.direction = direction
			target.get_hurt(damage)

			# 怪物攻击被闪避时，判定相关特效
			if data_player and data_player.has_effect("effect_000021"):
				var effect = data_player.get_effect("effect_000021")
				var hit_rate = effect.value
				# 概率加成
				hit_rate += luck_rate_add()
				if randf() < hit_rate:
					# 闪避追击
					data_player.execute_normal_attack_no_cd()

	elif skill is DataSpawnSkill:
		# 黑化邪龙的召唤导致了过度复杂的未知情况，停用处理
		if data_monster.is_black_monster:
			return
		# 召唤怪物
		for count in range(skill.monster_count):
			var random_index = randi_range(0,skill.monster_id_list.size()-1)
			var monster_id = skill.monster_id_list[random_index]
			var monster_config = monster_config_dic[monster_id]
			var monster = monster_manager.create_monster(
				monster_id, false, monster_config,monster_skill_dic,effect_config_dic
			)
			# 自动锁定玩家
			monster.auto_lock_player = true
			# 不掉落物品
			monster.is_drop_item = false
			# 标记为召唤物
			monster.is_spawn_monster = true
			# 随机一个位置
			var pos = Vector2(randf(), randf())
			add_monster(monster, pos)
	elif skill is DataEffectBuffSkill:
		var buff = skill.create_buff()
		if skill.target_type == 0:
			# 给玩家添加特殊效果
			data_player.add_buff(buff)
		elif skill.target_type == 1:
			# 给怪物添加特殊效果
			data_monster.add_buff(buff)
		elif skill.target_type == 3:
			# 给目标对象添加特殊效果
			target.add_buff(buff)

	_check_boss_combat_start(data_monster)


func _check_boss_combat_start(data_monster: DataMonster):
	# 开始计算击杀时间
	if data_monster.is_boss() and not is_boss_combat_start:
		is_boss_combat_start = true
		boss_combat_started.emit(self, data_monster)	


# 判断玩家是否有魔法屏障技能的buff，如果有，则调整伤害
func update_damage_by_effect(damage: DataDamage):
	var data_buff = data_player.get_buff_by_id("skill_000103")
	if data_buff:
		var effect = data_buff.get_effect(DataEffect.ManaDefense)
		if effect:
			# 计算可以转换的MP量（不能超过玩家当前MP）
			var can_convert_mp = min(effect.value, data_player.mp)
			# 计算剩余伤害
			var rest_damage = damage.value - can_convert_mp
			if rest_damage > 0:
				damage.value = rest_damage
				damage.damage_mp = can_convert_mp
			else:
				damage.value = 0
				damage.damage_mp = damage.value


## 获取总的怪物击杀数量
func get_total_kill_monster_count():
	var total_count = 0
	for count in kill_monster_count_dic.values():
		total_count += count
	return total_count


## 获取单个怪物击杀数量
func get_kill_monster_count(monster_id: String):
	return kill_monster_count_dic.get(monster_id, 0)


func get_player():
	return data_player


## 开始无尽之塔
func start_endless():
	endless_layer += 1
	# 根据层数确定怪物id
	for i in range(monster_count):
		var monster_id = monster_id_list[endless_layer-1]
		var monster_config = monster_config_dic[monster_id]
		var monster = monster_manager.create_monster(
			monster_id, false, monster_config,monster_skill_dic,effect_config_dic
		)
		# 强化怪物
		monster.upgrade(monster_upgrade)
		# 修改名称
		monster.name = monster.name + "(魔)"
		# 随机一个位置
		var pos = Vector2(randf(), randf())
		# 添加怪物
		add_monster(monster,pos)


func end_endless():
	# 结束无尽之塔
	endless_ended.emit(self)


func get_endless_reward():
	for reward in reward_list:
		if reward.type == "allot_point":
			data_player.add_allot_point(reward.value)


func is_endless_max():
	return endless_layer >= ENDLESS_LAYER_MAX


## 保存地图中怪物刷新时间信息
func save() -> Dictionary:
	var result = {}
	
	result["id"] = id
	if is_explored:
		result["is_explored"] = true
	if not monster_refresh_time_dic.is_empty():
		result["monster_refresh_time_dic"] = monster_refresh_time_dic
	if not kill_monster_count_dic.is_empty():
		result["kill_monster_count_dic"] = kill_monster_count_dic
	if not data_npcs.is_empty():
		result["data_npcs"] = {}
		for data_npc in data_npcs.values():
			result["data_npcs"][data_npc.id] = data_npc.save()
	return result


func load(data: Dictionary) -> void:
	id = data.id
	if data.has("is_explored"):
		is_explored = data.is_explored
	if data.has("monster_refresh_time_dic"):
		monster_refresh_time_dic = data.monster_refresh_time_dic
	if data.has("kill_monster_count_dic"):
		kill_monster_count_dic = data.kill_monster_count_dic
	if data.has("data_npcs"):
		for data_npc_id in data["data_npcs"].keys():
			var data_npc = data_npcs[data_npc_id]
			data_npc.load(data["data_npcs"][data_npc_id])
